home *** CD-ROM | disk | FTP | other *** search
- // BEGIN FLOCK GPL
- //
- // Copyright Flock Inc. 2005-2007
- // http://flock.com
- //
- // This file may be used under the terms of of the
- // GNU General Public License Version 2 or later (the "GPL"),
- // http://www.gnu.org/licenses/gpl.html
- //
- // Software distributed under the License is distributed on an "AS IS" basis,
- // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- // for the specific language governing rights and limitations under the
- // License.
- //
- // END FLOCK GPL
- //
-
- // TODO JVL: queue is now a stack but the syntax continues to use 'queue'. Will fix later.
-
- // XPConnect Helpers
- var Cc = Components.classes;
- var Ci = Components.interfaces;
- var Cr = Components.results;
-
- // Constants
- const MYWORLD_SHORTNAME = "myworld";
- const MYWORLD_FULLNAME = "Myworld";
- const MYWORLD_TITLE = "Myworld Service";
- const MYWORLD_FAVICON = "http://www.flock.com/favicon.ico";
- const MYWORLD_CID = Components.ID("{bb35349f-fb23-3089-4d3d-28462e1c98eb}");
- const MYWORLD_CONTRACTID = "@flock.com/myworld-service;1";
- const CATEGORY_COMPONENT_NAME = "Myworld JS Component";
-
- const CACHESERVICE_CONTRACTID = "@mozilla.org/network/cache-service;1";
- const TIMER_CONTRACTID = "@mozilla.org/timer;1";
- const XMLHTTPREQUEST_CONTRACTID = "@mozilla.org/xmlextras/xmlhttprequest;1";
-
- const MAX_FLICKR_FARMS = 10;
- const PROTOCOL = "http://";
- const FLICKR_FARM = "farm";
- const MAX_RETRIEVAL_ATTEMPTS = 3;
- const DEFAULT_INTERVAL = 500; // in milliseconds
- const DEFAULT_TIMER_TYPE = Ci.nsITimer.TYPE_REPEATING_PRECISE;
-
-
-
- // Component Utils (just used for the timer/scheduler)
- var gCompTK;
- function getCompTK() {
- if (!gCompTK) {
- var gCompTK = Cc["@flock.com/singleton;1"]
- .getService(Ci.flockISingleton)
- .getSingleton("chrome://browser/content/flock/services/common/load-compTK.js")
- .wrappedJSObject;
- }
- return gCompTK;
- }
-
-
-
- // ===========================================================
- // ========== BEGIN flockMyworldService Component ============
- // ===========================================================
-
-
- // BEGIN Constructor
- function myworldService()
- {
- this._logger = Cc["@flock.com/logger;1"].createInstance(Ci.flockILogger);
- this._logger.init("MyworldService");
-
- this._logger.debug("BEGIN Ctor");
- this._queue = [];
-
- this._ctk = {
- interfaces: [
- "nsISupports",
- "nsIClassInfo",
- "nsISupportsCString",
- "nsIObserver",
- "flockIMyworldService"
- ],
- shortName: MYWORLD_SHORTNAME,
- fullName: MYWORLD_FULLNAME,
- description: MYWORLD_TITLE,
- favicon: MYWORLD_FAVICON,
- CID: MYWORLD_CID,
- contractID: MYWORLD_CONTRACTID
- };
-
- this.obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
- this.obs.addObserver(this, "xpcom-shutdown", false);
- this.obs.addObserver(this, "flock-data-ready", false);
-
- this._coop = Cc["@flock.com/singleton;1"]
- .getService(Ci.flockISingleton)
- .getSingleton("chrome://browser/content/flock/common/load-faves-coop.js")
- .wrappedJSObject;
-
- var inst = this; // this is necessary as you cannot use 'this' within the defined notify function
- var callback = { notify : function(aTimer) { inst.processQueue(); } };
- this._timer = Components.classes[TIMER_CONTRACTID].createInstance(Ci.nsITimer);
- this._timer.initWithCallback(callback, DEFAULT_INTERVAL, DEFAULT_TIMER_TYPE);
-
- this._logger.info("Myworld service created");
- this._logger.debug("END Ctor");
- }
- // END Constructor
-
- myworldService.prototype.init =
- function myworldService_init()
- {
- var loosePhotosEnum = this._coop.RichPhoto.all();
- while (loosePhotosEnum.hasMoreElements()) {
- var photo = loosePhotosEnum.getNext();
- if (!photo.cachedThumbnail || photo.cachedThumbnail == "") {
- this.queueResource(photo.id());
- }
- }
- }
-
- // BEGIN nsIObserver
- myworldService.prototype.observe = function (subject, topic, data)
- {
- if (topic == "xpcom-shutdown")
- {
- this.obs.removeObserver(this, "xpcom-shutdown");
- } else if (topic == "flock-data-ready") {
- this.obs.removeObserver(this, "flock-data-ready");
- this.init();
- }
- }
- // END nsIObserver
-
-
- // BEGIN flockIMyworldService
- myworldService.prototype.flags = Ci.nsIClassInfo.SINGLETON;
-
- // queue a unique resource for processing
- myworldService.prototype.queueResource =
- function myworldService_queueResource(aURN)
- {
- this._logger.debug("BEGIN queueResource: " + aURN);
- if (!this.queueResourceExists(aURN))
- {
- this.createQueueResource(aURN);
- }
- this._logger.debug("END queueResource: " + aURN);
- }
-
-
- // clear the queue
- myworldService.prototype.clearQueue =
- function myworldService_clearQueue()
- {
- while(this._queue.length > 0)
- {
- this._queue.shift();
- }
- this._logger.info("queue cleared");
- }
-
-
- // check the browser cache for the given key
- // returns a valid cached key or null if not found
- myworldService.prototype.checkCache =
- function myworldService_checkCache(aKey)
- {
- this._logger.debug("BEGIN checkCache: " + aKey);
- if (aKey)
- {
- var cacheSession = this.getCacheSession();
- if (cacheSession)
- {
- // check in the browser cache for the given key
- if (this.isValidCacheEntry(cacheSession, aKey))
- {
- this._logger.debug("END checkCache: " + aKey);
- return aKey;
- }
-
- // we couldn't find the given resource in the browser cache, it may have been forwarded to a farm...
- // trim the url(key) for our search needs and search for this modified url(key)
- var keyBase = aKey;
- if (aKey.indexOf(PROTOCOL) == 0)
- {
- keyBase = aKey.substr(PROTOCOL.length);
- }
-
- for (var idx = 1; idx < MAX_FLICKR_FARMS + 1; idx++)
- {
- var key = PROTOCOL + FLICKR_FARM + idx + "." + keyBase;
- if (this.isValidCacheEntry(cacheSession, key))
- {
- this._logger.debug("END checkCache: " + key);
- return key;
- }
- }
- }
- }
-
- this._logger.debug("END checkCache: null");
- return null;
- }
- // END flockIMyworldService
-
-
- // BEGIN flockMyworldService
- // returns the cache session or null if an error occured
- myworldService.prototype.getCacheSession =
- function myworldService_getCacheSession()
- {
- var cacheService = Cc[CACHESERVICE_CONTRACTID].getService(Ci.nsICacheService);
- if (!cacheService)
- {
- this._logger.debug("getCacheSession: No cache service");
- return null;
- }
-
- // check both memory and disk cache
- // NOTE: 'HTTP' is the name for the browser cache
- var cacheSession = cacheService.createSession("HTTP", Ci.nsICache.STORE_ANYWHERE, true);
- if (!cacheSession)
- {
- this._logger.debug("getCacheSession: No cache session");
- return null;
- }
-
- cacheSession.doomEntriesIfExpired = false;
- return cacheSession;
- }
-
-
- // check that the given key is in the browser cache
- // returns true if found, false otherwise
- myworldService.prototype.isValidCacheEntry =
- function isValidCacheEntry(cacheSession, aKey)
- {
- this._logger.debug("BEGIN isValidCacheEntry: " + aKey);
- if (!cacheSession)
- {
- this._logger.debug("END isValidCacheEntry: No cache session");
- return false;
- }
-
- if (!aKey || aKey.length < 1)
- {
- this._logger.debug("END isValidCacheEntry: No key supplied");
- return false;
- }
-
- try
- {
- // checks browser cache for given key(url)
- // found if a cacheEntryDescriptor is returned and the resource's size is > 0,
- // otherwise it will throw an exception
- var cacheEntryDescriptor = cacheSession.openCacheEntry(aKey, Ci.nsICache.ACCESS_READ, false);
- var found = this.isNonEmptyResource(cacheEntryDescriptor);
- var msg = "END isValidCacheEntry: openCacheEntry: " + aKey + (found ? " (found in cache)" : " (not found in cache)");
- this._logger.debug(msg);
- return found;
- }
- catch (ex)
- {
- this._logger.debug("isValidCacheEntry: " + aKey + " (not in cache)");
- }
-
- this._logger.debug("END isValidCacheEntry: false");
- return false;
- }
-
-
- // helper function to determine if the cacheEntryDescriptor is:
- // 1) not null
- // 2) size > 0
- // also reports additional details if showDetails = true
- // returns true if it passes the above criteria, false otherwise
- myworldService.prototype.isNonEmptyResource =
- function isNonEmptyResource(cacheEntryDescriptor, showDetails)
- {
- if (showDetails)
- this._logger.debug("isNonEmptyResource:");
-
- // NOTE: when checking cacheEntryDescriptor for nullness, we need to check it explictly against null. '!cacheEntryDescriptor' will not work!
- var found = (cacheEntryDescriptor != null);
-
- if (showDetails)
- this._logger.debug(" descriptor: " + (found ? "not null" : "null"));
-
- var size = cacheEntryDescriptor.dataSize;
-
- if (showDetails)
- this._logger.debug(" size: " + size);
-
- return found && (size > 0);
- }
-
-
- // process the elements stored in the queue
- myworldService.prototype.processQueue =
- function processQueue()
- {
- var elem = null;
- var coopObj = null;
- var thumbnail = null;
-
- while (1)
- {
- //this._logger.debug("BEGIN processQueue: current queue size: " + this._queue.length);
- if (this._queue.length < 1)
- {
- //this._logger.debug("END processQueue: nothing in the queue to process");
- return;
- }
-
- // pop the first entry off the queue for processing
- elem = this._queue.shift();
- if (!elem)
- {
- this._logger.debug("END processQueue: element popped off queue is null, nothing to process");
- return;
- }
-
- this._logger.debug("processQueue:");
- this._logger.debug(" processing: " + elem.urn);
- this._logger.debug(" items left in queue: " + this._queue.length);
-
- // retrieve the appropriate resource from the rdf in memory
- coopObj = this._coop.get(elem.urn);
- if (!coopObj)
- {
- this._logger.debug("END processQueue: " + elem.urn + " (not in rdf)");
- return;
- }
-
- thumbnail = coopObj.thumbnail;
- this._logger.debug("processQueue: thumbnail: " + thumbnail);
-
- // if the retrieved resource doesn't have topmedia as a parent,
- // discard from the queue and just set the regular thumbnail in hopes it can show something
- if (this.belongsToTopMedia(coopObj))
- {
- this._logger.debug("processQueue: " + elem.urn + " (found in topmedia)");
- break;
- }
- else
- {
- this._logger.debug("processQueue: " + elem.urn + " (not in topmedia, process the next item in the queue)");
- coopObj.cachedThumbnail = thumbnail;
- }
- }
-
- // we only need to update the rdf with the cachedThumbnail if it doesn't already exist
- if (!this.isValidCachedThumbnail(coopObj.cachedThumbnail, thumbnail))
- {
- // take a look in the browser cache for the cached thumbnail url
- thumbnail = this.checkCache(thumbnail);
- if (thumbnail)
- {
- this._logger.debug("processQueue: " + thumbnail + " (in cache)");
-
- // found the cached thumbnail url, now store it in the rdf; we're done!
- coopObj.cachedThumbnail = thumbnail;
- }
- else
- {
- this._logger.debug("processQueue: " + coopObj.thumbnail + " (not in cache)");
- coopObj.cachedThumbnail = "";
-
- // we couldn't find the cached thumbnail url in the browser cache so retrieve the resource
- // from the social service to see if we can cache it ourselves and retrieve it later
- this.reRequestResource(coopObj.thumbnail);
-
- this._logger.debug("processQueue: re-add to queue:");
- this._logger.debug(" URN: " + elem.urn);
- this._logger.debug(" current attempt: " + elem.attempt);
-
- // since it wasn't in the browser cache this time, re-add the queued entry to the end of the
- // queue; we will try retrieving it later (for a maximum of MAX_RETRIEVAL_ATTEMPTS) otherwise,
- // just fail as there might be something wrong with the social service itself
- this.requeueResource(elem, coopObj)
- }
- }
-
- this._logger.debug("END processQueue");
- }
-
-
- // checks to see if the coop object has a topmedia as a parent
- // returns true if topmedia is a parent, false otherwise
- myworldService.prototype.belongsToTopMedia =
- function myworldService_belongsToTopMedia(coopObj)
- {
- if (!coopObj)
- {
- return false;
- }
-
- var parents = coopObj.getParents();
- if (!parents)
- {
- return false;
- }
-
- for (var idx in parents)
- {
- var parent = parents[idx];
- if (parent)
- {
- var grandParents = parent.getParents();
- if (grandParents)
- {
- for (var idxGP in grandParents)
- {
- var grandParent = grandParents[idxGP];
- if (grandParent.id() == "urn:flock:topmedia")
- {
- this._logger.debug("belongsToTopMedia: belongs to topmedia!");
- return true;
- }
- }
- }
- }
- }
-
- return false;
- }
-
-
- // check if the cachedThumbnail is:
- // 1) not null or empty and
- // 2) not the loading picture
- // returns true if the cachedThumbnail passes the above criteria, false otherwise
- myworldService.prototype.isValidCachedThumbnail =
- function myworldService_isValidCachedThumbnail(cachedThumbnail, thumbnail)
- {
- return cachedThumbnail && cachedThumbnail.length > 0;
- }
-
-
- // re-retrieve the resource from the social service to see if we can cache it ourselves and retrieve it later
- myworldService.prototype.reRequestResource =
- function myworldService_reRequestResource(thumbnail)
- {
- this._logger.debug("BEGIN reRequestResource");
- if (thumbnail)
- {
- var request = Cc[XMLHTTPREQUEST_CONTRACTID].createInstance(Ci.nsIXMLHttpRequest);
- if (!request)
- {
- this._logger.debug("END reRequestResource: unable to instantiate HTTPRequest");
- return;
- }
-
- try
- {
- this._logger.debug("reRequestResource: HTTPRequest sent for " + thumbnail);
- request.open("GET", coopObj.thumbnail, true);
- request.send(null);
- }
- catch (ex)
- {
- this._logger.debug("reRequestResource: HTTPRequest failed for " + thumbnail);
- }
- }
- this._logger.debug("END reRequestResource");
- }
-
-
- // requeue the resource unless the maximum attempts to retrieve the resource has been exceeded
- myworldService.prototype.requeueResource =
- function myworldService_requeueResource(elem, coopObj)
- {
- this._logger.debug("BEGIN requeueResource");
- elem.attempt++;
- if (elem.attempt < MAX_RETRIEVAL_ATTEMPTS)
- {
- this._queue.unshift(elem);
- this._logger.debug("requeueResource: " + elem.urn + " (re-added to queue successfully)");
- }
- else
- {
- // number of attempts exceeded, just set the displayed thumbnail to the original resource
- coopObj.cachedThumbnail = coopObj.thumbnail;
- this._logger.debug("requeueResource: " + elem.urn + " (not re-added to queue as we've already tried " + MAX_RETRIEVAL_ATTEMPTS + " times)");
- }
-
- this._logger.debug(" number of items in queue: " + this._queue.length);
- this._logger.debug("END requeueResource");
- }
-
-
- // check if the resource already exists in the queue to be processed
- // returns false if it is unique to the queue, true otherwise
- myworldService.prototype.queueResourceExists =
- function myworldService_queueResourceExists(aURN)
- {
- this._logger.debug("BEGIN queueResourceExists: " + aURN);
- if (!aURN || aURN.length < 1)
- {
- this._logger.debug("END queueResourceExists: true (empty string)");
- return true;
- }
-
- // ensure we don't add duplicates to the queue
- // NOTE: This may become an issue if there are an excessive number of items in the queue
- for (var idx = 0; idx < this._queue.length; idx++)
- {
- if (this._queue[idx].urn == aURN)
- {
- this._logger.debug("END queueResourceExists: true (already exists)");
- return true;
- }
- }
-
- this._logger.debug("END queueResourceExists: false");
- return false;
- }
-
-
- // create a new resource and add it to the service queue to be processed
- myworldService.prototype.createQueueResource =
- function myworldService_createQueueResource(aURN)
- {
- if (aURN && aURN.length > 0)
- {
- // add a new url/retrieval attempts (key/value) pair to the queue
- var obj = {};
- obj.urn = aURN;
- obj.attempt = 0;
- this._logger.debug("createQueueResource:");
- this._logger.debug(" URN: " + obj.urn);
- this._logger.debug(" attempt: " + obj.attempt);
- this._queue.unshift(obj);
- }
- }
- // END flockMyworldService
-
-
- // ========== END flockMyworldService Component ============
-
-
-
- // ================================================
- // ========== BEGIN XPCOM Module support ==========
- // ================================================
-
- const ENABLE_DEBUG = false; // switch to turn off slow debug code for production
- function DEBUG(x) { if (ENABLE_DEBUG) debug("flockMyworldService: "+x+"\n"); }
-
- function createModule(aParams) {
- DEBUG("createModule called\n CID: " + aParams.CID + "\n componentName: " + aParams.componentName + "\n contractID: " + aParams.contractID + "\n componentClass: " + aParams.componentClass);
- return {
- registerSelf: function (aCompMgr, aFileSpec, aLocation, aType) {
- aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
- aCompMgr.registerFactoryLocation( aParams.CID, aParams.componentName,
- aParams.contractID, aFileSpec,
- aLocation, aType );
- var catMgr = Cc["@mozilla.org/categorymanager;1"]
- .getService(Ci.nsICategoryManager);
- catMgr.addCategoryEntry( "flock-startup", aParams.componentName,
- "service,"+aParams.contractID, true, true );
- if (!aParams.categories) { aParams.categories = []; }
- for (var i = 0; i < aParams.categories.length; i++) {
- var cat = aParams.categories[i];
- catMgr.addCategoryEntry( cat.category, cat.entry,
- cat.value, true, true );
- }
- },
- getClassObject: function (aCompMgr, aCID, aIID) {
- if (!aCID.equals(aParams.CID)) { throw Cr.NS_ERROR_NO_INTERFACE; }
- if (!aIID.equals(Ci.nsIFactory)) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; }
- return { // Factory
- createInstance: function (aOuter, aIID) {
- if (aOuter != null) { throw Cr.NS_ERROR_NO_AGGREGATION; }
- var comp = new aParams.componentClass();
- if (aParams.implementationFunc) { aParams.implementationFunc(comp); }
- rv = comp.QueryInterface(aIID);
- return rv;
- }
- };
- },
- canUnload: function (aCompMgr) { return true; }
- };
- }
-
- // NS Module entrypoint
- function NSGetModule(aCompMgr, aFileSpec) {
- DEBUG("NSGetModule called");
- return createModule({
- componentClass: myworldService,
- CID: MYWORLD_CID,
- contractID: MYWORLD_CONTRACTID,
- componentName: CATEGORY_COMPONENT_NAME,
- implementationFunc: function (aComp) { getCompTK().addAllInterfaces(aComp); },
- categories: [
- ]
- });
- }
-
-
- // ========== END XPCOM Module support ==========
-